//
//  Camera.m
//  CompGraf2
//
//  Created by Juan Niosi on 16/08/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Camera.h"
#import "Engine.h"
#import "project.h"

@implementation Camera

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        GLfloat *pos = malloc(sizeof(GLfloat)*3);
        pos[0] = 0.0f;
        pos[1] = 0.0f;
        pos[2] = 0.0f;
        
        position = pos;
        
        GLfloat *rot = malloc(sizeof(GLfloat)*3);
        rot[0] = 0.0f;
        rot[1] = 0.0f;
        rot[2] = 0.0f;
        
        rotation = rot;
        
        viewportMatrix[0] = 0;
        viewportMatrix[1] = 0;
        viewportMatrix[2] = 1;
        viewportMatrix[3] = 1;
    }
    
    return self;
}

- (id)init:(Engine *)engine
{
    self = [super init];
    if (self) {
        // Initialization code here.
        GLfloat *pos = malloc(sizeof(GLfloat)*3);
        pos[0] = 0.0f;
        pos[1] = 0.0f;
        pos[2] = 0.0f;
        
        position = pos;
        
        GLfloat *rot = malloc(sizeof(GLfloat)*3);
        rot[0] = 0.0f;
        rot[1] = 0.0f;
        rot[2] = 0.0f;
        
        rotation = rot;
        
        viewportMatrix = [engine viewport];
    }
    
    return self;
}

- (void)drawPos:(Engine *)engine
{
    [engine set_transform];
    
    [engine translate:position];
    [engine rotate:rotation];
    
    planes = [self getCameraPlanes];
}

- (void)reset:(Engine *)engine
{
    /*
    GLfloat *pos = malloc(sizeof(GLfloat)*3);
    pos[0] = position[0] * -1;
    pos[1] = position[1] * -1;
    pos[2] = position[2] * -1;
    [engine cameraPosition:pos];
    
    GLfloat *rot = malloc(sizeof(GLfloat)*3);
    rot[0] = rotation[0] * -1;
    rot[1] = rotation[1] * -1;
    rot[2] = rotation[2] * -1;
    [engine cameraRotation:rot];
     */
    [engine reset_transform];
}

- (void)translate:(GLfloat *)translation
{
    position[0] += translation[0];
    position[1] += translation[1];
    position[2] += translation[2];
    
    NSLog(@"campos");
}

- (void)rotate:(GLfloat *)rot
{
    rotation[0] += rot[0];
    rotation[1] += rot[1];
    rotation[2] += rot[2];
}

- (GLfloat *)getCameraPlanes
{
    
    glGetFloatv(GL_PROJECTION_MATRIX, projMatrix);
    
    glGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix);
    
    GLfloat *PMV = [self matrixProduct:projMatrix :modelMatrix];
    
    GLfloat *camPlanes = malloc(sizeof(GLfloat)*24);
    
    for (int i = 0; i < 4; i++)
    {
        camPlanes[i] = PMV[i] + PMV[i+12];      //left
        camPlanes[4+i] = -PMV[i] + PMV[i+12];   //right
        
        camPlanes[8+i] = PMV[i+4] + PMV[i+12];  //top
        camPlanes[12+i] = -PMV[i+4] + PMV[i+12];//bottom
        
        camPlanes[16+i] = PMV[i+8] + PMV[i+12]; //far
        camPlanes[20+i] = -PMV[i+8] + PMV[i+12];//near
    }
    
    return camPlanes;
}

- (GLfloat *)matrixProduct:(GLfloat *)A:(GLfloat *)B
{
    GLfloat *result = malloc(sizeof(GLfloat)*16);
    
    for (int c = 0; c < 4; c++)
    {
        for (int r = 0; r < 4; r++)
        {
            float sum = 0;
            for (int x = 0; x < 4; x++)
            {
                sum += A[c + x*4] * B[x + r*4];
            }
            result[c*4 + r] = sum;
        }
    }
    
    return result;
}

- (BOOL)isVisible:(Bounds *)bounds
{
    float x;
    float y;
    float z;
    for (int i = 0; i < 2; i++)
    {
        if (i == 0)
            x = bounds.xMin;
        else
            x = bounds.xMax;
        
        for (int j = 0; j < 2; j++)
        {
            if (j == 0)
                y = bounds.yMin;
            else
                y = bounds.yMax;
            
            for (int k = 0; k < 2; k++)
            {
                if (k == 0)
                    z = bounds.zMin;
                else
                    z = bounds.zMax;
                
                if ([self pointInside:x :y :z])
                {
                    return true;
                }
            }
        }
    }
    return false;
}

- (BOOL)pointInside:(GLfloat)x:(GLfloat)y:(GLfloat)z
{
    for (int i=0; i < 6; i++)
    {
        float sum = 0;
        sum += planes[i*4]*x;
        sum += planes[i*4 + 1]*y;
        sum += planes[i*4 + 2]*z;
        sum += planes[i*4 + 3];
        if (sum < 0)
            return false;
    }
    return true;
}

- (Vector3 *)unproject:(CGPoint)point:(GLfloat)winZ
{
    GLfloat x,y,z;
    gluUnProject(point.x, viewportMatrix[3]-point.y, winZ, modelMatrix, projMatrix, viewportMatrix, &x, &y, &z);
    
    return [[Vector3 alloc] init:x:y:z];
}

-(void)dealloc
{
    free(planes);
    free(projMatrix);
    free(modelMatrix);
    free(viewportMatrix);
    [super dealloc];
}
            
@end
